package com.admin_system.service.impl;

import com.admin_system.config.TemplateConfigProvider;
import com.admin_system.entity.StudentData;
import com.admin_system.entity.TemplateConfig;
import com.admin_system.service.ExcelTemplateService;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.nio.charset.StandardCharsets;
import java.nio.file.DirectoryStream;
import java.nio.file.StandardOpenOption;

/**
 * Excel模板服务实现类
 * 该类实现了ExcelTemplateService接口，包含处理Excel数据的具体逻辑。
 */
@Service
public class ExcelTemplateServiceImpl implements ExcelTemplateService {

    private static final Logger logger = LoggerFactory.getLogger(ExcelTemplateServiceImpl.class);

    private final TemplateConfigProvider templateConfigProvider;

    public ExcelTemplateServiceImpl() {
        this.templateConfigProvider = new TemplateConfigProvider();
    }

    /**
     * 处理Excel数据并返回处理结果的字符串信息
     *
     * @param dataFile        前端上传的Excel数据文件
     * @param replacementText 用于替换表头括号内文字的文本
     * @return 处理结果的字符串信息
     * @throws IOException 处理文件时可能抛出的IO异常
     */
    @Override
    public String processData(MultipartFile dataFile, String replacementText) throws IOException {
        // This method likely shouldn't exist or should call the main one with pdf=false
        // For now, let's assume it's not the primary entry point for download
        logger.warn("processData called directly, PDF conversion will not be performed.");
        Map<String, Object> result = processDataAndZipWithOverSizedClasses(dataFile, replacementText, false);
        // This method is expected to return a String, not a Resource... adapting slightly
        List<String> oversized = (List<String>) result.get("overSizedClasses");
        if (oversized != null && !oversized.isEmpty()) {
            return "文件处理部分成功，但以下班级因人数过多未处理: " + String.join(", ", oversized);
        }
        return "文件处理成功。";
    }

    /**
     * 处理Excel数据并返回压缩包资源
     *
     * @param dataFile        前端上传的Excel数据文件
     * @param replacementText 用于替换表头括号内文字的文本
     * @return 包含所有生成Excel文件的压缩包资源
     * @throws IOException 处理文件时可能抛出的IO异常
     */
    @Override
    public Resource processDataAndZip(MultipartFile dataFile, String replacementText) throws IOException {
        // Call the main method with includePdf = false
        Map<String, Object> result = processDataAndZipWithOverSizedClasses(dataFile, replacementText, false);
        return (Resource) result.get("resource");
    }

    /**
     * 处理Excel数据并返回压缩包资源以及超过人数限制的班级信息
     *
     * @param dataFile        前端上传的Excel数据文件
     * @param replacementText 用于替换表头括号内文字的文本
     * @return Map包含压缩包资源和超过人数限制的班级列表
     * @throws IOException 处理文件时可能抛出的IO异常
     */
    @Override
    public Map<String, Object> processDataAndZipWithOverSizedClasses(MultipartFile dataFile, String replacementText, boolean includePdf) throws IOException {
        // ...(keep existing validation and data reading)
        if (dataFile == null || dataFile.isEmpty()) {
            throw new IllegalArgumentException("请提供数据表文件！");
        }
        logger.info("开始处理Excel数据: 文件名={}, 大小={}, 替换文本={}, 包含PDF={}",
                dataFile.getOriginalFilename(), dataFile.getSize(), replacementText, includePdf);
        List<StudentData> studentDataList = readExcelData(dataFile.getInputStream());
        if (studentDataList.isEmpty()) {
            throw new IllegalArgumentException("数据表为空或缺少必要列：姓名、班级、学号");
        }

        Path tempDir = Files.createTempDirectory("excel_export_" + UUID.randomUUID().toString());
        logger.debug("创建临时目录: {}", tempDir);
        List<String> over100Classes = new ArrayList<>();
        List<Path> generatedExcelFiles = new ArrayList<>(); // Keep track of generated Excels specifically

        try {
            // 按班级分组存储学生数据
            Map<String, List<StudentData>> classDataMap = new HashMap<>();
            for (StudentData data : studentDataList) {
                String className = data.getClassName();
                if (className != null && !className.trim().isEmpty()) {
                    classDataMap.computeIfAbsent(className, k -> new ArrayList<>()).add(data);
                }
            }
            logger.info("数据按班级分组完成，共 {} 个班级。", classDataMap.size());

            // 遍历每个班级的数据生成Excel
            for (Map.Entry<String, List<StudentData>> entry : classDataMap.entrySet()) {
                String className = entry.getKey();
                List<StudentData> classData = entry.getValue();
                int total = classData.size();
                logger.debug("处理班级: {}, 人数: {}", className, total);

                Integer selectedTemplate = selectTemplate(total);
                if (selectedTemplate == null) {
                    logger.warn("班级 {} 人数过多 ({})，跳过处理。", className, total);
                    over100Classes.add(className + " (因人数超过100人上限，未处理)");
                    continue;
                }

                TemplateConfig config = templateConfigProvider.getTemplateConfig(selectedTemplate);
                Path excelOutputPath = tempDir.resolve(sanitizeFilename(className) + ".xlsx"); // Sanitize filename

                try (InputStream templateStream = getTemplateStream(config.getFilePath()); // Use try-with-resources for template stream
                     Workbook wb = new XSSFWorkbook(templateStream)) { // Load template
                    Sheet ws = wb.getSheetAt(0);

                    replaceHeaderText(ws, replacementText);
                    writeData(ws, classData, config);
                    adjustColumnWidth(ws);

                    try (FileOutputStream fileOut = new FileOutputStream(excelOutputPath.toFile())) {
                        wb.write(fileOut);
                    }
                    generatedExcelFiles.add(excelOutputPath); // Add to list of generated Excels
                    logger.debug("成功生成Excel文件: {}", excelOutputPath);
                } catch (FileNotFoundException fnfEx) {
                    logger.error("模板文件未找到: {}", config.getFilePath(), fnfEx);
                    throw new IOException("所需的模板文件丢失: " + config.getFilePath(), fnfEx);
                } catch (Exception ex) {
                    logger.error("处理班级 {} 的Excel时出错: {}", className, ex.getMessage(), ex);
                    // Decide how to handle: skip class, add to error list, or fail fast?
                    // For now, let's add to over100 to indicate failure for this class
                    over100Classes.add(className + " (处理失败: " + ex.getMessage() + ")");
                    continue; // Continue to next class
                }
            } // End of class loop

            // --- PDF Conversion Step ---
            if (includePdf && !generatedExcelFiles.isEmpty()) {
                logger.info("请求包含PDF，开始调用转换工具...");
                Path pythonExePath = templateConfigProvider.getPythonExePath();

                if (Files.exists(pythonExePath)) {
                    try {
                        String conversionResult = runPythonConverter(tempDir.toString(), pythonExePath.toString());
                        logger.info("Python PDF转换工具执行结果:\n{}", conversionResult);
                        // Check conversionResult for specific error patterns if needed
                    } catch (IOException | InterruptedException e) {
                        logger.error("调用Python PDF转换工具时出错", e);
                        // Add a general warning to the over100Classes list?
                        over100Classes.add("警告：PDF转换步骤执行失败 (" + e.getMessage() + ")");
                        Thread.currentThread().interrupt(); // Reset interrupt status
                    } catch (Exception e) {
                        logger.error("调用Python PDF转换工具时发生意外错误", e);
                        over100Classes.add("警告：PDF转换步骤发生意外错误 (" + e.getMessage() + ")");
                    }
                } else {
                    logger.error("Python PDF转换工具未找到，路径: {}", pythonExePath);
                    over100Classes.add("警告：未找到PDF转换工具，无法生成PDF");
                }
            } else if (includePdf) {
                logger.warn("请求包含PDF，但没有成功生成的Excel文件可供转换。");
            }


            // --- Zipping Step ---
            logger.info("开始打包文件...");
            Path zipFilePath = tempDir.resolve("学号导出_" + replacementText + ".zip"); // More descriptive zip name
            List<Path> filesToZip = new ArrayList<>();
            // Use DirectoryStream to find all relevant files in tempDir
            try (DirectoryStream<Path> stream = Files.newDirectoryStream(tempDir, "*.{xlsx,pdf,txt}")) {
                for (Path entry : stream) {
                    filesToZip.add(entry);
                }
            }

            // Add warning file if needed
            Path warningFilePath = null;
            if (!over100Classes.isEmpty()) {
                warningFilePath = tempDir.resolve("【请注意】部分班级处理说明.txt");
                StringBuilder message = new StringBuilder("以下班级未能按预期处理，请检查具体原因：\r\n\r\n"); // Use Windows line breaks for Notepad
                for (String detailedMessage : over100Classes) { // Use the detailed message directly
                    message.append("- ").append(detailedMessage).append("\r\n");
                }
                // Add extra info
                message.append("\r\n可能原因包括：\r\n")
                        .append("* 班级人数超过系统支持的最大模板人数 (100人)。\r\n")
                        .append("* 处理该班级Excel文件时发生内部错误。\r\n")
                        .append("* PDF转换步骤失败或未找到转换工具 (如果勾选了导出PDF)。\r\n");

                Files.writeString(warningFilePath, message.toString(), StandardCharsets.UTF_8, StandardOpenOption.CREATE);
                filesToZip.add(warningFilePath); // Add warning file to the list
                logger.warn("生成了处理说明文件，包含 {} 个班级/处理步骤的说明信息。", over100Classes.size());
            }


            // Check if there's anything to zip at all
            if (filesToZip.isEmpty()) {
                logger.error("没有生成任何文件可供打包。");
                throw new IOException("处理完成，但未能生成任何可导出的文件。");
            }

            // Create and fill the ZIP file
            try (ZipOutputStream zipOutputStream = new ZipOutputStream(new FileOutputStream(zipFilePath.toFile()))) {
                for (Path file : filesToZip) {
                    // Exclude the zip file itself if it's somehow listed (shouldn't happen with current filter)
                    if (!file.equals(zipFilePath)) {
                        logger.debug("添加到ZIP: {}", file.getFileName());
                        zipOutputStream.putNextEntry(new ZipEntry(file.getFileName().toString()));
                        Files.copy(file, zipOutputStream);
                        zipOutputStream.closeEntry();
                    }
                }
            }
            logger.info("ZIP文件创建成功: {}", zipFilePath);

            byte[] zipData = Files.readAllBytes(zipFilePath);

            Map<String, Object> result = new HashMap<>();
            result.put("resource", new ByteArrayResource(zipData));
            result.put("overSizedClasses", over100Classes); // Still return this for potential header/message

            logger.info("处理完成，返回ZIP资源。");
            return result;
        } catch (IOException e) {
            logger.error("处理过程中发生IO异常", e);
            // Ensure tempDir is cleaned up even on error before throwing
            cleanupTempFiles(tempDir);
            throw e; // Re-throw original IO exception
        } catch (Exception e) {
            logger.error("处理过程中发生意外异常", e);
            cleanupTempFiles(tempDir);
            // Wrap other exceptions in IOException or a custom exception type
            throw new IOException("处理Excel数据时发生意外错误: " + e.getMessage(), e);
        } finally {
            // Cleanup in finally block is crucial
            cleanupTempFiles(tempDir);
        }
    }

    /**
     * Helper method to run the Python Excel to PDF converter.
     * Adapts logic from ExcelToPdfConverterTest.
     *
     * @param excelFolderPath Path to the folder containing Excel files.
     * @param pythonExePath   Path to the python executable.
     * @return String containing the output and errors from the process.
     * @throws IOException          If an I/O error occurs.
     * @throws InterruptedException If the process is interrupted.
     */
    private String runPythonConverter(String excelFolderPath, String pythonExePath)
            throws IOException, InterruptedException {
        logger.info("执行Python转换器: exe='{}', folder='{}'", pythonExePath, excelFolderPath);
        List<String> command = new ArrayList<>();
        command.add(pythonExePath);
        command.add(excelFolderPath);

        ProcessBuilder processBuilder = new ProcessBuilder(command);
        // Set the working directory for the python script if necessary
        // processBuilder.directory(new File(pythonExeParentDir));

        Process process = processBuilder.start();

        // Use try-with-resources for readers
        StringBuilder output = new StringBuilder();
        StringBuilder error = new StringBuilder();
        try (BufferedReader outputReader = new BufferedReader(new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8));
             BufferedReader errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream(), StandardCharsets.UTF_8))) {

            String line;
            while ((line = outputReader.readLine()) != null) {
                output.append(line).append("\n");
            }
            while ((line = errorReader.readLine()) != null) {
                error.append(line).append("\n");
            }
        } // Readers are closed automatically

        int exitCode = process.waitFor();
        logger.info("Python转换器执行完毕，退出码: {}", exitCode);

        StringBuilder result = new StringBuilder();
        result.append("Python Process Exit Code: ").append(exitCode).append("\n");
        if (output.length() > 0) {
            result.append("Standard Output:\n").append(output).append("\n");
        }
        if (error.length() > 0) {
            // Log Python errors as warnings or errors in Java log
            logger.warn("Python Process Error Output:\n{}", error);
            result.append("Error Output:\n").append(error);
        }
        if (exitCode != 0) {
            logger.error("Python PDF转换失败，退出码非0: {}", exitCode);
            // Consider throwing an exception or returning a specific error status
        }

        return result.toString();
    }

    /**
     * Sanitizes a string to be used as a filename by removing or replacing invalid characters.
     * Basic implementation, might need refinement based on specific OS requirements.
     *
     * @param inputName The original name (e.g., class name).
     * @return A sanitized string suitable for use as a filename.
     */
    private String sanitizeFilename(String inputName) {
        if (inputName == null) return "Unnamed";
        // Remove or replace characters invalid for filenames in Windows/Linux/Mac
        // Common invalid chars: / \ : * ? " < > |
        // Replace with underscore or remove. Simple replacement here:
        return inputName.trim().replaceAll("[/\\\\:*?\"<>|]", "_");
    }

    /**
     * 从输入流中读取Excel文件的学生数据
     *
     * @param inputStream 包含Excel数据的输入流
     * @return 学生数据列表
     * @throws IOException 读取文件时可能发生的IO异常
     */
    private List<StudentData> readExcelData(InputStream inputStream) throws IOException {
        List<StudentData> dataList = new ArrayList<>();
        try (Workbook workbook = new XSSFWorkbook(inputStream)) {
            Sheet sheet = workbook.getSheetAt(0);
            if (sheet == null) {
                throw new IOException("无法读取Excel文件，工作表不存在。");
            }
            int headerRowIndex = 0; // Assuming header is first row
            Row headerRow = sheet.getRow(headerRowIndex);
            if (headerRow == null) {
                throw new IOException("无法读取表头行。");
            }

            Map<String, Integer> headerMap = new HashMap<>();
            for (Cell cell : headerRow) {
                String headerValue = getStringCellValue(cell);
                if (headerValue != null) {
                    headerMap.put(headerValue.trim(), cell.getColumnIndex());
                }
            }
            logger.debug("检测到的表头映射: {}", headerMap);

            Integer nameCol = headerMap.get("姓名");
            Integer classCol = headerMap.get("班级");
            Integer idCol = headerMap.get("学号");

            if (nameCol == null || classCol == null || idCol == null) {
                logger.error("输入Excel缺少必要的列。需要'姓名', '班级', '学号'. 检测到的表头: {}", headerMap.keySet());
                throw new IllegalArgumentException("输入Excel缺少必要的列：需要'姓名', '班级', '学号'.");
            }

            // Log first few rows raw data for debugging
            int logRowCount = 0;
            logger.debug("--- 开始读取Excel数据行 ---");
            for (int rowIndex = headerRowIndex + 1; rowIndex <= sheet.getLastRowNum(); rowIndex++) {
                Row row = sheet.getRow(rowIndex);
                // Log raw cell values for the first 5 data rows
                if (logRowCount < 5) {
                    String rawName = (row != null && row.getCell(nameCol) != null) ? getStringCellValue(row.getCell(nameCol)) : "<null_cell>";
                    String rawClass = (row != null && row.getCell(classCol) != null) ? getStringCellValue(row.getCell(classCol)) : "<null_cell>";
                    String rawId = (row != null && row.getCell(idCol) != null) ? getStringCellValue(row.getCell(idCol)) : "<null_cell>";
                    logger.debug("读取 Raw Row [{}]: Name={}, Class={}, ID={}", rowIndex + 1, rawName, rawClass, rawId);
                }
                logRowCount++;

                if (row == null) {
                    logger.trace("跳过空行: {}", rowIndex + 1);
                    continue;
                }

                String name = getStringCellValue(row.getCell(nameCol));
                String className = getStringCellValue(row.getCell(classCol));
                String id = getStringCellValue(row.getCell(idCol));

                // Check for non-empty values AFTER trimming
                boolean nameValid = name != null && !name.trim().isEmpty();
                boolean classValid = className != null && !className.trim().isEmpty();
                boolean idValid = id != null && !id.trim().isEmpty();

                if (nameValid && classValid && idValid) {
                    dataList.add(new StudentData(name.trim(), className.trim(), id.trim()));
                } else {
                    logger.trace("跳过行 {}，因为包含无效值: NameValid={}, ClassValid={}, IDValid={} (Values: '{}', '{}', '{}')",
                            rowIndex + 1, nameValid, classValid, idValid, name, className, id);
                }
            }
            logger.debug("--- 结束读取Excel数据行 ---");
        } catch (Exception e) {
            logger.error("读取Excel数据时出错", e);
            throw new IOException("读取上传的Excel文件时出错: " + e.getMessage(), e);
        }
        logger.info("成功从Excel读取 {} 条有效学生数据。", dataList.size());
        return dataList;
    }

    /**
     * 获取单元格的字符串值
     *
     * @param cell Excel单元格
     * @return 单元格内容的字符串表示，如果单元格为空或格式不支持则返回null
     */
    private String getStringCellValue(Cell cell) {
        if (cell == null) return null;
        switch (cell.getCellType()) {
            case STRING:
                return cell.getStringCellValue();
            case NUMERIC:
                // Handle potential decimal values if ID is numeric but might have .0
                if (DateUtil.isCellDateFormatted(cell)) {
                    // Handle dates if necessary, otherwise treat as number
                    return String.valueOf(cell.getNumericCellValue()); // Or format date
                } else {
                    // Format as plain number string, avoid scientific notation for long IDs
                    DataFormatter formatter = new DataFormatter();
                    return formatter.formatCellValue(cell);
                    // Previous simple conversion: return String.valueOf((long) cell.getNumericCellValue());
                }
            case BLANK:
                return null; // Explicitly handle blank
            case BOOLEAN:
                return String.valueOf(cell.getBooleanCellValue());
            case FORMULA:
                // Evaluate formula if possible, handle errors
                try {
                    switch (cell.getCachedFormulaResultType()) {
                        case STRING:
                            return cell.getStringCellValue();
                        case NUMERIC:
                            return String.valueOf(cell.getNumericCellValue());
                        case BOOLEAN:
                            return String.valueOf(cell.getBooleanCellValue());
                        default:
                            return null;
                    }
                } catch (Exception e) {
                    logger.warn("无法计算公式单元格: {}", cell.getAddress(), e);
                    return "#ERR";
                }
            default:
                return null;
        }
    }

    /**
     * 根据班级学生总数选择合适的模板
     *
     * @param total 班级学生总数
     * @return 合适的模板人数上限，如果没有合适的模板则返回null
     */
    private Integer selectTemplate(int total) {
        List<Integer> sortedSizes = new ArrayList<>(templateConfigProvider.getTemplateConfigMap().keySet());
        Collections.sort(sortedSizes);
        for (Integer size : sortedSizes) {
            if (total <= size) {
                return size;
            }
        }
        logger.warn("未找到适合人数 {} 的模板。", total);
        return null;
    }

    /**
     * 替换工作表表头括号内的文字
     *
     * @param ws              工作表对象
     * @param replacementText 用于替换的文本
     */
    private void replaceHeaderText(Sheet ws, String replacementText) {
        Row headerRow = ws.getRow(0); // Assuming header is always row 0
        if (headerRow != null) {
            for (Cell cell : headerRow) {
                if (cell != null && cell.getCellType() == CellType.STRING) {
                    String cellValue = cell.getStringCellValue();
                    if (cellValue != null && cellValue.contains("(") && cellValue.contains(")")) {
                        // More robust replacement: find last ( and first ) after it
                        int startIndex = cellValue.lastIndexOf('(');
                        int endIndex = cellValue.indexOf(')', startIndex);
                        if (startIndex != -1 && endIndex != -1 && startIndex < endIndex) {
                            String newValue = cellValue.substring(0, startIndex + 1) + replacementText + cellValue.substring(endIndex);
                            cell.setCellValue(newValue);
                            logger.trace("替换表头: '{}' -> '{}'", cellValue, newValue);
                        }
                    }
                }
            }
        } else {
            logger.warn("模板文件中未找到表头行 (Row 0)。");
        }
    }

    /**
     * 将班级学生数据写入工作表
     *
     * @param ws        工作表对象
     * @param classData 班级学生数据列表
     * @param config    模板配置信息
     */
    private void writeData(Sheet ws, List<StudentData> classData, TemplateConfig config) {
        int startRow = config.getDataStart() - 1; // 0-based
        // int lastDataRow = config.getLastDataRow() - 1; // 0-based, inclusive - CURRENT LOGIC

        // --- 还原旧版计算逻辑 START ---
        int lastDataRow_1based = config.getLastDataRow(); // Get the 1-based value from config
        int numRowsPerColumn = lastDataRow_1based - startRow + 1; // Calculate like the old version (e.g., for 100 template: 51 - 2 + 1 = 50)
        logger.debug("使用旧版计算逻辑: StartRow(0-based)={}, LastDataRow(1-based)={}, RowsPerCol={}",
                startRow, lastDataRow_1based, numRowsPerColumn); // Log the calculation basis
        // --- 还原旧版计算逻辑 END ---

        // logger.trace("写入数据: StartRow={}, LastDataRow={}, RowsPerCol={}", startRow, lastDataRow, numRowsPerColumn); // Keep old trace commented
        logger.debug("--- 开始写入模板数据 (共 {} 条) ---", classData.size());

        // --- 修改点：在writeData内部直接设置样式 --- START
        // Create shared style components once outside the loop for efficiency
        Workbook wb = ws.getWorkbook();
        CellStyle dataCellStyle = wb.createCellStyle();
        dataCellStyle.setAlignment(HorizontalAlignment.CENTER);
        dataCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        dataCellStyle.setBorderTop(BorderStyle.THIN);
        dataCellStyle.setBorderBottom(BorderStyle.THIN);
        dataCellStyle.setBorderLeft(BorderStyle.THIN);
        dataCellStyle.setBorderRight(BorderStyle.THIN);

        Font dataFont = wb.createFont();
        dataFont.setFontName("宋体");
        dataFont.setFontHeightInPoints((short) 12);
        dataCellStyle.setFont(dataFont);
        // --- 修改点：在writeData内部直接设置样式 --- END

        for (int idx = 0; idx < classData.size(); idx++) {
            StudentData data = classData.get(idx);

            // --- 还原旧版计算逻辑 START ---
            int targetRow = startRow + (idx >= numRowsPerColumn ? idx - numRowsPerColumn : idx);
            int baseColumn = idx < numRowsPerColumn ? 1 : 5;
            // --- 还原旧版计算逻辑 END ---

            // Debug log for first 2 students specifically
            if (idx < 2 || idx == 40) { // Log for idx=0, 1, and the problematic 40
                logger.debug("写入 Student Index [{}]: TargetRow={}, BaseCol={}, Class={}, Name={}, ID={}",
                        idx, targetRow, baseColumn, data.getClassName(), data.getName(), data.getId());
            }

            // Ensure row exists
            Row row = ws.getRow(targetRow);
            if (row == null) {
                row = ws.createRow(targetRow);
            }

            // Ensure cells exist and write data using getCellEnsured for ALL indices
            Cell classCell = getCellEnsured(row, baseColumn);
            classCell.setCellValue(data.getClassName());
            classCell.setCellStyle(dataCellStyle); // Apply style

            Cell nameCell = getCellEnsured(row, baseColumn + 1);
            nameCell.setCellValue(data.getName());
            nameCell.setCellStyle(dataCellStyle); // Apply style

            Cell idCell = getCellEnsured(row, baseColumn + 2);
            idCell.setCellValue(data.getId());
            idCell.setCellStyle(dataCellStyle); // Apply style
        }
        logger.debug("--- 结束写入模板数据 (样式已在内部应用) ---");
    }

    // Helper to get or create a cell
    private Cell getCellEnsured(Row row, int cellIndex) {
        Cell cell = row.getCell(cellIndex);
        if (cell == null) {
            cell = row.createCell(cellIndex);
        }
        return cell;
    }

    /**
     * 调整工作表的列宽以适应内容
     *
     * @param ws 工作表对象
     */
    private void adjustColumnWidth(Sheet ws) {
        // Adjust specific columns used for data
        int[] dataColumnIndices = {1, 2, 3, 5, 6, 7};
        for (int colIdx : dataColumnIndices) {
            ws.autoSizeColumn(colIdx);
            // Optional: Set a minimum width if autoSize is too narrow
            int currentWidth = ws.getColumnWidth(colIdx);
            ws.setColumnWidth(colIdx, Math.max(currentWidth, 3000)); // Approx 10-12 chars width
        }
        logger.trace("调整列宽: Cols {}", dataColumnIndices);
    }

    /**
     * 清理临时文件和目录
     *
     * @param tempDir 临时目录路径
     */
    private void cleanupTempFiles(Path tempDir) {
        if (tempDir == null || !Files.exists(tempDir)) {
            return;
        }
        logger.info("开始清理临时目录: {}", tempDir);
        try {
            Files.walk(tempDir)
                    .sorted(Comparator.reverseOrder()) // Delete files before directories
                    .map(Path::toFile)
                    // .peek(f -> logger.trace("尝试删除: {}", f.getAbsolutePath())) // Verbose logging
                    .forEach(file -> {
                        if (!file.delete()) {
                            logger.warn("无法删除临时文件/目录: {}", file.getAbsolutePath());
                            // Might consider adding to a list of undeleted files if crucial
                        }
                    });
            logger.info("临时目录清理完成: {}", tempDir);
        } catch (IOException e) {
            logger.error("清理临时文件目录时出错: {}, 原因: {}", tempDir, e.getMessage(), e);
        }
    }

    private InputStream getTemplateStream(String filePath) throws IOException {
        InputStream inputStream = getClass().getClassLoader().getResourceAsStream(filePath);
        if (inputStream == null) {
            throw new FileNotFoundException("模板文件未找到: " + filePath);
        }
        return inputStream;
    }
}

/// / 模板配置提供类
//class TemplateConfigProvider {
//    private static final Map<Integer, TemplateConfig> TEMPLATE_CONFIG;
//
//    static {
//        TemplateConfigBuilder builder = new TemplateConfigBuilder();
//        String basePath = "templates/";
//        builder.addConfig(40, basePath + "模板表-40.xlsx", 3, 21);
//        builder.addConfig(50, basePath + "模板表-50.xlsx", 3, 26);
//        builder.addConfig(60, basePath + "模板表-60.xlsx", 3, 31);
//        builder.addConfig(70, basePath + "模板表-70.xlsx", 3, 36);
//        builder.addConfig(80, basePath + "模板表-80.xlsx", 3, 41);
//        builder.addConfig(90, basePath + "模板表-90.xlsx", 3, 46);
//        builder.addConfig(100, basePath + "模板表-100.xlsx", 3, 51);
//        TEMPLATE_CONFIG = builder.build();
//    }
//
//    public Map<Integer, TemplateConfig> getTemplateConfigMap() {
//        return TEMPLATE_CONFIG;
//    }
//
//    public TemplateConfig getTemplateConfig(int maxSize) {
//        return TEMPLATE_CONFIG.get(maxSize);
//    }
//
//    public Path getPythonExePath() {
//        return Path.of("src/main/resources/python-exe/excel_to_pdf.exe");
//    }
//
//    // 模板配置建造者类，提供链式配置方法
//    private static class TemplateConfigBuilder {
//        /**
//         * 存储配置信息的Map
//         */
//        private final Map<Integer, TemplateConfig> configMap = new HashMap<>();
//
//        /**
//         * 链式添加模板配置
//         * @param maxSize 人数上限
//         * @param filePath 模板文件路径
//         * @param dataStart 数据开始行（1-based）
//         * @param lastDataRow 最后数据行（1-based）
//         * @return 建造者实例
//         */
//        public TemplateConfigBuilder addConfig(int maxSize, String filePath, int dataStart, int lastDataRow) {
//            configMap.put(maxSize, new TemplateConfig(filePath, dataStart, lastDataRow));
//            return this;
//        }
//
//        /**
//         * 构建模板配置映射
//         * @return 不可变的模板配置映射
//         */
//        public Map<Integer, TemplateConfig> build() {
//            return Collections.unmodifiableMap(configMap);
//        }
//    }
